#include "../cse452.h"
#include "InteractiveUI.h"
#include "InteractiveInterface.h"
#include <FL/Fl.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_File_Chooser.H>
#include <FL/gl.h>

InteractiveUI::InteractiveUI() :
ibar( scene.changeCamera() )
{
    widget.SetPosition( Point3(0,0,0) );
    widget.SetScale( 0.5 );
	activeObject = 0;
}

InteractiveUI::~InteractiveUI() {
}

void InteractiveUI::resize(int w, int h) {
    scene.resize(w, h);
}

void InteractiveUI::draw() {
    setup3DDrawing( scene.getBackground(), scene.getCamera().getWidth(), scene.getCamera().getHeight(), false );

    // Sets up the camera and model view matrices
    glMatrixMode(GL_PROJECTION);
    glLoadMatrixd( &ibar.getCamera().getProjection()(0,0) );    
    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixd( &ibar.getCamera().getWorldToCamera()(0,0) );    

    glPushAttrib( GL_ALL_ATTRIB_BITS );
    // Start drawing at the top of the tree
    scene.draw();
    glPopAttrib();

    if ( interactiveUI->m_bWidget->value() ) {
        glEnable( GL_LIGHT0 );
        widget.DrawWidget();
    }

    if ( interactiveUI->m_bIBar->value() )
        ibar.Draw( bIsMouseOverIBar, interactiveUI->m_bIBarHelp->value() ? true : false );

    endDrawing();
}

int InteractiveUI::handle(int event) {
    static bool s_bDoingCamera = false;
    bool bRedraw = false;
	const bool bRightButton = Fl::event_button()==3 || Fl::event_state( FL_SHIFT ) || Fl::event_button3();
	
    switch (event) {
        case FL_SHORTCUT: {
            if ( interactiveUI->m_bInteractive->value() ) {
                scene.changeCamera().moveKeyboard();
                RedrawWindow();
            }
            break;
        } return 1;
        case FL_PUSH: {
            if ( bRightButton == false && interactiveUI->m_bIBar->value() ) {
                s_bDoingCamera = ibar.MouseDown( ScreenPoint( Fl::event_x(), Fl::event_y() ), Fl::event_state(FL_SHIFT) ? true : false );
            } else {
                s_bDoingCamera = false;
            }
            if ( s_bDoingCamera == false && bRightButton == false && interactiveUI->m_bWidget->value() ) {
                widget.MouseDown( scene.getCamera(), ScreenPoint( Fl::event_x(), Fl::event_y() ) );
            }
        } return 1;
        case FL_DRAG: {
            if ( s_bDoingCamera == true ) {
                ibar.MouseDrag( ScreenPoint( Fl::event_x(), Fl::event_y() ), Fl::event_state(FL_SHIFT) ? true : false );
            } else if ( s_bDoingCamera == false && bRightButton == false && interactiveUI->m_bWidget->value() ) {
                const Matrix4 mat = widget.MouseDrag( scene.getCamera(), ScreenPoint( Fl::event_x(), Fl::event_y() ) );
                if ( interactiveUI->m_bWidgetMove->value()) {
                    scene.moveSelectedNode( mat );
                }
            }
            RedrawWindow();
              } return 1;
        case FL_MOVE: 
            if ( interactiveUI->m_bIBar->value() ) 
                bIsMouseOverIBar = ibar.MouseMove( ScreenPoint( Fl::event_x(), Fl::event_y() ), bRedraw );
            if ( s_bDoingCamera == false && bRightButton == false && interactiveUI->m_bWidget->value() ) 
                if ( widget.MouseMove( scene.getCamera(), ScreenPoint( Fl::event_x(), Fl::event_y() ) ) )
                    bRedraw = true;

            if ( bRedraw == true )
                RedrawWindow();
            return 1;
        case FL_RELEASE: 
            if ( bRightButton == true ) {
                selectObject(event);
            }
			if ( s_bDoingCamera == true && interactiveUI->m_bIBar->value() ) 
                ibar.MouseRelease( ScreenPoint( Fl::event_x(), Fl::event_y() ), Fl::event_state(FL_SHIFT) ? true : false );
            if ( s_bDoingCamera == false && bRightButton == false && interactiveUI->m_bWidget->value() ) 
                widget.MouseRelease( scene.getCamera(), ScreenPoint( Fl::event_x(), Fl::event_y() ) );

            

            s_bDoingCamera = false;
            RedrawWindow();
            return 1;
    }
    return 0;
}

// Selects the object if one is under the click location and orients the widget to it
// Returns 0 if selects object, 1 if no object is found
int InteractiveUI::selectObject(int event){
	// ToDo: select object and orient widget
	//Make ray
	std::cout << "Selecting (" << Fl::event_x() << "," << Fl::event_y() << ") (" << this->getWidth() << "," << this->getHeight() << ")" << std::endl;
	Vector3 dir = scene.makeRay(Fl::event_x(), Fl::event_y());
	Point3 eyePoint = scene.getCamera().getEye();

	//Find objects it intersects
	double bestT = DBL_MAX, t, u, v;
	MatrixObject* bestMatrixObject = 0;
	std::list<MatrixObject>* objects = scene.getObjectList();
	Point3 objPoint, hitPoint;
	Vector3 objDir, hitNormal;
	HitRecord objHits;
	
	for(std::list<MatrixObject>::iterator it = objects->begin(); it != objects->end(); ++it){
		objDir = it->im * dir;
		objPoint = it->im * eyePoint;
		objHits = it->o->intersect(objPoint, objDir);
		objHits.sortHits();
		if(objHits.getFirstHit(t, u, v, hitPoint, hitNormal)){//found hit
#ifdef _DEBUG
			std::cout << "Hit object at ";
			hitPoint.print();
			std::cout << std::endl;
#endif
			if(t < bestT && t > 0){
				bestT = t;
				bestMatrixObject = &(*it);
			}
		}
	}
	if(bestMatrixObject != 0){
		int refCount = 0;
		for(std::list<MatrixObject>::iterator it = objects->begin(); it != objects->end(); ++it)
			refCount += (bestMatrixObject->o == it->o) ? 1 : 0;
		std::cout << "Object reference count: " << refCount << std::endl;
		if(refCount != 1){
#ifdef _DEBUG
			std::cout << "Found not unique object, copying" << std::endl;
#endif
			bestMatrixObject->o = new Object(bestMatrixObject->o);
			bestMatrixObject->inTree = false;
		}//TODO at here
		if(activeObject != 0)
			activeObject->o->setEmission(Color(activeObject->o->getEmission()[0] - static_cast<GLfloat>(0.6), 
												activeObject->o->getEmission()[1] - static_cast<GLfloat>(0.4), 
												activeObject->o->getEmission()[2]));
		activeObject = bestMatrixObject;
		activeObject->o->setEmission(Color(activeObject->o->getEmission()[0] + static_cast<GLfloat>(0.6), 
												activeObject->o->getEmission()[1] + static_cast<GLfloat>(0.4), 
												activeObject->o->getEmission()[2]));
		orientWidget();
		return 0;
	}
	
	return 1;
}

void InteractiveUI::orientWidget(){
	widget.setActiveObject(activeObject);
	widget.SetPosition(activeObject->m * Point3(0., 0., 0.));
	widget.SetAxis(activeObject->m * Vector3(1., 0., 0.), activeObject->m * Vector3(0., 1., 0.));
	widget.SetScale(
		((activeObject->m * Point3(0.,0.,0.))
		- (activeObject->m * Point3(1./3.,1./3.,1./3))).length() * 2.
		);
}

void InteractiveUI::readScene(const char *fname) {
    std::string filename(fname);
    if (!scene.loadSceneFile(filename)) {
        std::string message = "Error loading scene file: \"" + filename + "\":\n" + scene.getErrorMessage();
        interactiveUI->m_txtStatus->value(message.c_str());
    } else {
        std::string message = "Scene file: \"" + filename + "\" loaded succesfully.";
        interactiveUI->m_txtStatus->value(message.c_str());
    }
	activeObject = 0;
    RedrawWindow();
}

